import { createVNode, ref, render, getCurrentInstance, nextTick } from 'vue';
import { MyDrawer, type MyDrawerInstance } from './drawer';
import type { App, ComponentInternalInstance, SetupContext } from 'vue';
import type { HookDrawerProps } from './types';

let _app: App;

export const useDrawer = () => {
  let _drawerInstance: ComponentInternalInstance;
  const drawerRef = ref<MyDrawerInstance>();
  const appContext = _app?._context || getCurrentInstance()?.appContext;
  // 当前模态框是否处于App.vue上下文中
  const isAppChild = ref(false);

  const getDrawerInstance = async () => {
    await nextTick();
    if (isAppChild.value && drawerRef.value) {
      return drawerRef.value;
    }

    if (_drawerInstance) {
      return _drawerInstance;
    }
    const container = document.createElement('div');
    const vnode = createVNode(MyDrawer);
    vnode.appContext = appContext;
    render(vnode, container);
    _drawerInstance = vnode.component!;
    _drawerInstance.props.closeDrawer = hide;
    return _drawerInstance;
  };

  const setProps = async (_props) => {
    const instance = await getDrawerInstance();
    if (Object.is(instance, drawerRef.value)) {
      // @ts-ignore
      instance?.setProps?.(_props);
    } else {
      // @ts-ignore
      instance?.exposed?.setProps?.(_props);
    }
  };

  const hide = () => {
    setProps({ open: false });
  };

  const show = async (props: HookDrawerProps) => {
    setProps({
      ...props,
      closeDrawer: hide,
      open: true,
    });

    await nextTick();
  };

  const DrawerRender = (props: HookDrawerProps, { attrs, slots }: SetupContext) => {
    isAppChild.value = true;
    return <MyDrawer ref={drawerRef} {...{ ...attrs, ...props }} v-slots={slots} />;
  };

  DrawerRender.show = show;
  DrawerRender.hide = hide;
  DrawerRender.setProps = setProps;

  // ;[show, hide].forEach(fn => DrawerRender[fn.name] = fn)

  return [DrawerRender, drawerRef] as const;
};

export type DrawInstance = ReturnType<typeof useDrawer>;

export const installUseDrawer = (app: App) => {
  _app = app;
};
